/*
 * main.c
 *
 *  Created on: 3 févr. 2011
 *      Author: aurelien
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netdb.h>
#include  "rtp.h"
#include "NALU_Manager/nalu.h"

struct rtpbits {
  unsigned int v:2;           /* version: 2 */
  unsigned int p:1;           /* is there padding appended: 0 */
  unsigned int x:1;           /* number of extension headers: 0 */
  unsigned int cc:4;          /* number of CSRC identifiers: 0 */
  unsigned int m:1;           /* marker: 0 */
  unsigned int pt:7;          /* payload type: 33 for MPEG2 TS - RFC 1890 */
  unsigned int sequence:16;   /* sequence number: random */
};

struct rtpheader {      /* in network byte order */
  struct rtpbits b;
  int timestamp;        /* start: random */
  int ssrc;             /* random */
};


struct sockaddr_in * serveurInitialisation (int domain, char* ipServer ,int port) ;
int createConnectedSocketTCP(char* ipServer, int port);
void transfertProxyClientStandard(int socketClient,int socketProxy);
void transfertClientProxyStandard(int socketClient,int socketProxy);
void transfertClientProxyOption(int socketClient,int socketProxy,char* outputBuffer);
void transfertClientProxySetup(int socketClient,int socketProxy,char* outputBuffer);
void getPortFromSetup(char* outputBufferTCP);
void transfertUDPClientVersServeur();
char* getrtp2(int fd, struct rtpheader *rh, int* lengthData, char * inputBuffer, int intputLen) ;


#define TAILLEBUFFER 1456
#define portProxy 10554 //Adresse vers l'autre proxy
#define port_ecoute_proxy 9554
#define port_echange_rtp1 55555
#define port_echange_rtp2 66666
// Adresse sur lequel le client va se connecter

int portClient1;
int portClient2;
char* addresseProxy = "148.60.11.241";//Adresse de l'autre proxy

int main(int argc, char** argv)
{
	/* Déclaration */
	//Port sur lequel notre proxy ecoute

	int ret;
	socklen_t clientlen;//Taille de la structure définissant le client
	struct hostent *hp;//Utilisé pour récupérer l'adresse IP du proxy à partir
	struct sockaddr_in client;//Structure définissant l'adresse du client
	int sock_attente;//Socket utilisé lors de l'attente
	int sock_proxy_client;//nouveau socket créé lors de la récéption d'une connexion
	int sock_proxy_proxy;//Socket entre les deux proxy
	char outputBufferTCP[TAILLEBUFFER];//Socket utilisé entre les deux proxies

	/* Création de la connection à l'autre proxy*/

	 hp = gethostbyname(addresseProxy);
	 if (hp==NULL){
		 perror("Machine proxy distante inconnue");
		 exit(0);
	 }

	/* Création de la connection en écoute */

	sock_attente = socket(AF_INET , SOCK_STREAM , 0);
    if(sock_attente < 0){
    	perror("Erreur lors de la création du socket vers le proxy");
    	exit(1);
    }
    clientlen = sizeof(client); //longeur de la structure client

    bzero(&client,clientlen); //remplit la structure avec des 0
    client.sin_family = AF_INET;//Type IP
    client.sin_addr.s_addr = INADDR_ANY;//N'importe quel IP
    client.sin_port = htons(port_ecoute_proxy);//On ecoute sur ce port

    ret = bind(sock_attente, (struct sockaddr *)&client, clientlen);//On fait le bind

    if(ret < 0){
    	perror("Erreur lors du binding");
    	exit(1);
    }
    //Initialisation du buffer de sortie

    while(1){
		/* Attente d'une connection */
	    //Nous attendons maintenant les connections
		listen(sock_attente,50);
		//On accepte la nouvelle connection, on en récupère le socket associé.
		sock_proxy_client = accept(sock_attente, (struct sockaddr *)&client, &clientlen);
		//Création du socket proxy
		sock_proxy_proxy = createConnectedSocketTCP(inet_ntoa( *((struct in_addr *)hp->h_addr )),portProxy);
	    memset (outputBufferTCP,0x00,TAILLEBUFFER);

		transfertClientProxyOption(sock_proxy_client,sock_proxy_proxy,outputBufferTCP);

        /* Retransmission du deuxième paquet 									*/
        /*																		*/
        /*																		*/
        /*								Cas du OPTION							*/
        /*																		*/
        /*																		*/
        if(strncmp("OPTIONS",outputBufferTCP,7) == 0){

        	/* Reception du OK, et retransmission vers le client */
        	transfertProxyClientStandard(sock_proxy_client,sock_proxy_proxy);

    		/* Transmission du describe et transmission vers le serveur*/
        	transfertClientProxyStandard(sock_proxy_client,sock_proxy_proxy);

            /* Transmission du SDP, et forward vers le client */
        	transfertProxyClientStandard(sock_proxy_client,sock_proxy_proxy);


            /*																		*/
            /*																		*/
            /*								Cas du DESCRIBE							*/
            /*																		*/
            /*																		*/
    		}else if(strncmp("DESCRIBE",outputBufferTCP,7) == 0){

    		/* Réception du OK avec le SDP du serveur et transmission au client */
            transfertProxyClientStandard(sock_proxy_client,sock_proxy_proxy);

    		/* Réception du SETUP du client et transmission au serveur */
            transfertClientProxySetup(sock_proxy_client,sock_proxy_proxy,outputBufferTCP);

            getPortFromSetup(outputBufferTCP);


    		/* Réception du OK du serveur et transmission au client */
        	transfertProxyClientStandard(sock_proxy_client,sock_proxy_proxy);

    		/* Réception du PLAY du client et transmission au serveur */
        	transfertClientProxyStandard(sock_proxy_client,sock_proxy_proxy);


    		/* Réception du OK du serveur et transmission au client */
        	transfertProxyClientStandard(sock_proxy_client,sock_proxy_proxy);

        	/* Retransmission du flux reçu du client vers le serveur */
        	transfertUDPClientVersServeur(inet_ntoa( *((struct in_addr *)hp->h_addr )));

    		/* Réception du TEARDOWN du client et transmission au serveur */
        	transfertClientProxyStandard(sock_proxy_client,sock_proxy_proxy);

    		/* Réception du OK du serveur et transmission au client */
        	transfertProxyClientStandard(sock_proxy_client,sock_proxy_proxy);


        }

		close(sock_proxy_client);
		close(sock_proxy_proxy );


	}


}


struct sockaddr_in * serveurInitialisation (int domain, char* ipServer ,int port) {
    /* Initialisation du sockaddr */
	struct sockaddr_in * server;
    server = malloc(sizeof(struct sockaddr_in));       /*  */
    server->sin_family = domain;                  /* Internet/IP */
    server->sin_addr.s_addr = inet_addr(ipServer);  /* IP address */
    server->sin_port = htons(port);       /* server port */
    return server;
}

int createConnectedSocketTCP(char* ipServer, int port){
	int sock;
	struct sockaddr_in* server;

	sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock == -1) {
	  perror("Failed to create socket");
	  exit(1);
	}
	server = serveurInitialisation(AF_INET,ipServer,port);
	/* Establish connection */
	if (connect(sock,
			server,
				sizeof(struct sockaddr_in)) != 0) {
	  perror("Failed to connect with server");
	  exit(1);
	}
	return sock;

}

void transfertClientProxyOption(int socketClient,int socketProxy,char* outputBuffer){
	char inputBufferTCP[TAILLEBUFFER];//Socket utilisé pour le transfert entre le proxy et le client
	int ret;
	memset (outputBuffer,0x00,TAILLEBUFFER);
    memset (inputBufferTCP,0x00,TAILLEBUFFER);
    ret = 1;
	while(ret!=0 && strstr (outputBuffer,"\r\n\r\n") == NULL && strcmp(inputBufferTCP,"\r\n")!=0){
			ret=recv(socketClient,inputBufferTCP,TAILLEBUFFER,0);
			inputBufferTCP[ret]='\0';
			strcat(outputBuffer,inputBufferTCP);
	}

	ret=send(socketProxy, outputBuffer, strlen(outputBuffer), 0) ;
    if(ret<0){
    	perror("Erreur de reception du client");
		exit(1);
    }
}



void transfertClientProxyStandard(int socketClient,int socketProxy){
	char inputBufferTCP[TAILLEBUFFER];//Socket utilisé pour le transfert entre le proxy et le client
	char outputBufferTCP[TAILLEBUFFER];//Socket utilisé entre les deux proxies
	int ret;

	memset (outputBufferTCP,0x00,TAILLEBUFFER);
    memset (inputBufferTCP,0x00,TAILLEBUFFER);
    ret = 1;
	while(ret!=0 && strstr (outputBufferTCP,"\r\n\r\n") == NULL && strcmp(inputBufferTCP,"\r\n")!=0){
			ret=recv(socketClient,inputBufferTCP,TAILLEBUFFER,0);
			inputBufferTCP[ret]='\0';
			strcat(outputBufferTCP,inputBufferTCP);
	}

	ret=send(socketProxy, outputBufferTCP, strlen(outputBufferTCP), 0) ;
    if(ret<0){
    	perror("Erreur de reception du client");
		exit(1);
    }
}



void transfertProxyClientStandard(int socketClient,int socketProxy){
	char inputBufferTCP[TAILLEBUFFER];//Socket utilisé pour le transfert entre le proxy et le client
	char outputBufferTCP[TAILLEBUFFER];//Socket utilisé entre les deux proxies
	int ret;

	memset (outputBufferTCP,0x00,TAILLEBUFFER);
    memset (inputBufferTCP,0x00,TAILLEBUFFER);
    ret = 1;
	while(ret!=0 && strstr (outputBufferTCP,"\r\n\r\n") == NULL && strcmp(inputBufferTCP,"\r\n")!=0){
			ret=recv(socketProxy,inputBufferTCP,TAILLEBUFFER,0);
			inputBufferTCP[ret]='\0';
			strcat(outputBufferTCP,inputBufferTCP);
	}

	ret=send(socketClient, outputBufferTCP, strlen(outputBufferTCP), 0) ;
    if(ret<0){
    	perror("Erreur de reception du client");
		exit(1);
    }
}

void transfertClientProxySetup(int socketClient,int socketProxy,char* outputBuffer){
	char inputBufferTCP[TAILLEBUFFER];//Socket utilisé pour le transfert entre le proxy et le client
	int ret;

    memset (inputBufferTCP,0x00,TAILLEBUFFER);
    memset (outputBuffer,0x00,TAILLEBUFFER);
    ret = 1;
	while(ret!=0 && strstr (outputBuffer,"\r\n\r\n") == NULL && strcmp(inputBufferTCP,"\r\n")!=0){
			ret=recv(socketClient,inputBufferTCP,TAILLEBUFFER,0);
			inputBufferTCP[ret]='\0';
			strcat(outputBuffer,inputBufferTCP);
	}

	ret=send(socketProxy, outputBuffer, strlen(outputBuffer), 0) ;
    if(ret<0){
    	perror("Erreur de reception du client");
		exit(1);
    }
}


void getPortFromSetup(char* outputBufferTCP){
	char* message;//Copie du buffer passer en paramètre afin de ne pas modifier le message à renvoyer
	char* port;//Chaîne de cractère temporaire contenant les messages
	char* ligneInteressante;//C'est la ligne qui correspond aux infos que l'on veux
	int i,j;//Compteur caractère, et ligne
	message = malloc(TAILLEBUFFER*sizeof(char));
	memset (message,0x00,TAILLEBUFFER);
	//Copie du buffer d'entrée
	strcpy(message,outputBufferTCP);
	//Compteur global
	i=1;
	j=0;
	//Compteur de ligne
	while(message[i]!='\0'){
		if(message[i-1]=='\r' && message[i]=='\n'){
			//Ligne que l'on veut traiter
			if(j==2){
				ligneInteressante = message+(i*sizeof(char));
				port=strchr(ligneInteressante,'=')+1;
				port = strtok (port,"-");
			    portClient1=atoi(port);
			    port = strtok (NULL, " ,.-");
			    portClient2=atoi(port);

			}
			j++;
		}
		i++;
	}



}
void transfertUDPClientVersServeur(char* ipServer){
	int sockUdp;

	 struct hostent *hp;
	 struct sockaddr_in server; //structure serveur
	 struct sockaddr_in client; //structure client
	 socklen_t serverlen; //longueur de la structure sockaddr (serveur)
	 socklen_t clientlen; //longeur de la structure sockaddr client
	 char * buffer;
	 int ret;
	 char* urlServeur = "localhost";
	 int sockUdpClient1;
	 int sockUdpClient2;

	 hp = gethostbyname(urlServeur);
	 if (hp==NULL){
		 perror("Machine distante inconnue");
		 exit(0);
	 }


	////////////////////////////////////////////////
	//CONNECTION AVEC LAUTRE SERVEUR
	////////////////////////////////////////////////

	/* Connection UDP en attente du proxy serveur */
	sockUdp = socket(AF_INET, SOCK_DGRAM, 0);
	 //Création du socket UDP pour la récéption du flux de donnée
	serverlen = sizeof(server); //longeur de la structure server
	bzero(&server,serverlen); //remplit la structure avec des 0
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = INADDR_ANY;
	server.sin_port = htons(port_echange_rtp1); //port au bon encodage de donnees

	//Déclaration socket vers notre client
	sockUdpClient1 = socket(AF_INET, SOCK_DGRAM, 0);
	 //Création du socket UDP pour la récéption du flux de donnée
	clientlen = sizeof(client);
	bzero(&client,clientlen); //remplit la structure avec des 0
	client.sin_family = AF_INET;
	client.sin_addr.s_addr = inet_addr("127.0.0.1");
	client.sin_port = htons(portClient1); //port au bon encodage de donnees

	//Lien du descripteur de socket avec la structure server
	ret = bind(sockUdp, (struct sockaddr *)&server, serverlen);
	if (ret < 0){
		perror("Binding impossible");
		exit(0);
	}

	if(sockUdp==-1){
		perror("Failed to create socket");
		exit(1);
	}

	////////////////////////////////////////////////////////
	//// RECEPETION / TRAITEMENT / RETRANSMISSION
	/////////////////////////////////////////////////

	 int rtplength;

	//Déclaration des pointeurs utilisés par les fonctions de traitement des flux
	int* longueurPayLoad;
	char* payload;
	struct rtpheader *rh ;
	Nalu_info* nalu;
	//Allocations des pointeurs utilisés par les fonctions de traitement des flux
	rh = malloc(sizeof(struct rtpheader));
	payload = malloc(sizeof(char)*TAILLEBUFFER);
	longueurPayLoad = malloc(sizeof(int));
	nalu = malloc(sizeof(Nalu_info));
	buffer = malloc(sizeof(char)*TAILLEBUFFER);

	bzero(longueurPayLoad,sizeof(int));
	bzero(nalu,sizeof(Nalu_info));
	bzero(buffer,sizeof(char)*TAILLEBUFFER);
	 ret=-1;
	short doubleInterface = 1;
	while (1){

		 //Activé le Sender Report - Goodbye
		 rtplength = recvfrom(sockUdp, buffer, TAILLEBUFFER,0,  (struct sockaddr *)&server, &serverlen);
		 ret = sendto(sockUdpClient1, buffer, rtplength, 0, (struct sockaddr *)&client, clientlen);
		 if(ret == -1){perror("Erreur sendto");exit(-1);}
		 /*if(rtplength != 0)payload= getrtp2(sockUdp, rh, longueurPayLoad,buffer,rtplength);
////////////////////////////////////////////////////////////////////////////////////////
											//TODO
////////////////////////////////////////////////////////////////////////////////////////

		fill_nalu_struct_with_rtp_payload(payload, nalu);


		if(nalu->nalu.nalu_svc_hdr.dependency_id == 0 ){
			if(nalu->layer.DqId >= 16)printf("!");
			printf("Temporal ID   %u\n",nalu->nalu.nalu_svc_hdr.temporal_id);
		}else if (nalu->nalu.nalu_svc_hdr.dependency_id == 1 && doubleInterface){
			if(nalu->layer.DqId >= 32)printf("!");
						printf("Temporal ID   %u\n",nalu->nalu.nalu_svc_hdr.temporal_id);

						//Envoie via l'autre socket (ethernet)
		}*/


		 //res = recup_paquetRTP(buffer,rtplength,payloadRTP);
		bzero(buffer,sizeof(char)*TAILLEBUFFER);
		bzero(payload,sizeof(char)*TAILLEBUFFER);
		bzero(longueurPayLoad,sizeof(int));
		bzero(nalu,sizeof(Nalu_info));

	}
	free(rh);
	free(payload);
	free(longueurPayLoad);
	free(nalu);
	/////////////////////////////////////////////////
	// FIN RETRANSMISSION
	////////////////////////////////////////////////
}


char* getrtp2(int fd, struct rtpheader *rh, int* lengthData, char * inputBuffer, int intputLen) {
  unsigned int intP;
  char* charP = (char*) &intP;
  int headerSize;
  int lengthPacket;
  lengthPacket=intputLen;

  if (lengthPacket<0){
    printf("rtp: socket read error\n");
    exit(-1);
    }
  else if (lengthPacket<12){
    printf("rtp: packet too small (%d) to be an rtp frame (>12bytes)\n", lengthPacket);
    exit(-1);
    }
  if(lengthPacket<12) {
    *lengthData = 0;
    return 0;
  }

  rh->b.v  = (unsigned int) ((inputBuffer[0]>>6)&0x03);
  rh->b.p  = (unsigned int) ((inputBuffer[0]>>5)&0x01);
  rh->b.x  = (unsigned int) ((inputBuffer[0]>>4)&0x01);
  rh->b.cc = (unsigned int) ((inputBuffer[0]>>0)&0x0f);
  rh->b.m  = (unsigned int) ((inputBuffer[1]>>7)&0x01);
  rh->b.pt = (unsigned int) ((inputBuffer[1]>>0)&0x7f);
  intP = 0;
  memcpy(charP+2,&inputBuffer[2],2);
  rh->b.sequence = ntohl(intP);
  intP = 0;
  memcpy(charP,&inputBuffer[4],4);
  rh->timestamp = ntohl(intP);

  headerSize = 12 + 4*rh->b.cc; /* in bytes */

  *lengthData = lengthPacket - headerSize;
  return (char*) inputBuffer + headerSize;


}

